package net.cyndeline.GUI.rendering

import net.cyndeline.scalarlib.entityComponent.miranda.{Entity, EntityComponentManager}
import net.cyndeline.gameMap.TileContents
import net.cyndeline.entityComponent.components.gui.GraphicsComponent
import net.cyndeline.GUI.setup.SpriteReader
import net.cyndeline.GUI.graphics.Representation

/**
 * Examines a tile and creates new representations for entities that doesn't have any. Computes their pixel coordinates
 * based on grid coordinates and tile sprite size.
 */
class GraphicsParser(manager: EntityComponentManager, graphics: GraphicsContainer, tileWidth: Int, tileHeight: Int) {
  private val graphicsComponentType = manager.componentType(classOf[GraphicsComponent])

  /**
   * @param tile Tile to parse.
   * @param x X coordinate on the map grid.
   * @param y Y coordinate on the map grid.
   */
  def createRepresentations(tile: TileContents, x: Int, y: Int) {
    if (tile.agent.isDefined) {
      createRepresentation(tile.agent.get, x, y)
    }

    if (tile.item.isDefined) {
      createRepresentation(tile.item.get, x, y)
    }

    if (tile.trap.isDefined) {
      createRepresentation(tile.trap.get, x, y)
    }

    if (tile.decoration.isDefined) {
      createRepresentation(tile.decoration.get, x, y)
    }

    if (tile.decoration.isDefined) {
      createRepresentation(tile.decoration.get, x, y)
    }
  }

  private def createRepresentation(entity: Entity, x: Int, y: Int) {
    val currentRepresentation = graphics.getGraphic(entity)

    // No need to create representations for entities that already has them
    if (currentRepresentation.isDefined)
      return

    if (manager.hasComponent(entity, graphicsComponentType)) {
      val graphicsComponent = manager.getComponent(entity, graphicsComponentType)
      val representation = SpriteReader.makeRepresentation(graphicsComponent.graphicsId)
      addPixelCoordinates(representation, x, y)
      graphics.setGraphic(entity, representation)
    } else {
      throw new Error("The entity " + entity + " was present on the map position (" + x + "," + y + ") but did not have a graphics component.")
    }
  }

  /**
   * Computes where on the canvas a sprite representation should be drawn. This concerns the absolute position of the
   * sprite on the canvas, not modified by attempting to center the camera on a certain tile.
   *
   * Sprites are centered based on the leftmost side of a tile, as well as the bottom-most side. This means that sprites
   * (that aren't tiles) that exceed the tile in height will be aligned with the bottom of the tile it lies on, while
   * having its top spill over to the tile above. If the sprite exceeds the tile in width, it will spill over to the
   * right tile.
   *
   * @param x x coordinate on the map grid.
   * @param y y coordinate on the map grid.
   */
  private def addPixelCoordinates(r: Representation, x: Int, y: Int) {
    val upperLeftCornerX = x * tileWidth
    val upperLeftCornerY = y * tileHeight

    val yCoordinateAlignedWithBottom = upperLeftCornerY - (r.height - tileHeight)

    r.x = upperLeftCornerX
    r.y = yCoordinateAlignedWithBottom
  }
}
